// @Description
// @Author  xiaoyang
// @Date  2022/2/25 3:31 下午

package golang_rpc

import (
	"encoding/json"
	"fmt"
	"golang-rpc/codec"
	"io"
	"log"
	"net"
	"reflect"
	"sync"
)

const MagicNumber = 0x3bef5c

//	| Option{MagicNumber: xxx, CodecType: xxx} | Header{ServiceMethod ...} | Body interface{} |
//	| <------      固定 JSON 编码      ------>  | <-------   编码方式由 CodeType 决定   ------->|
type Option struct {
	MagicNumber int
	CodecType   codec.Type
}

var DefaultOption = &Option{
	MagicNumber: MagicNumber,
	CodecType:   codec.GobType,
}

// Server represents an RPC Server.
type Server struct{}

// NewServer returns a new Server.
func NewServer() *Server {
	return &Server{}
}

// DefaultServer is the default instance of *Server.
var DefaultServer = NewServer()

// Accept
/**
 * @Author yang
 * @Description 监听服务端请求，协程处理
 * @Date 10:54 上午 2022/2/27
 **/
func (server *Server) Accept(lis net.Listener) {
	for {
		// @Todo 监听请求
		conn, err := lis.Accept()
		log.Printf("接收连接请求:	%s \n", conn.RemoteAddr().String())
		if err != nil {
			log.Println("rpc server -- accept error :", err)
			return
		}
		// @Todo 协程处理
		go server.ServeCon(conn)
	}
}

//	Accept
/**
 * @Author yang
 * @Description	监听请求：
				DefaultServer 初始化一个默认的 Server 实例， 主要为了方便调用
			eg： lis，_ := net.Listen("tcp", ":8080")
				 golang_rpc.Accept()
 **/
func Accept(lis net.Listener) {
	DefaultServer.Accept(lis)
}

//	ServeCon
/**
 *  @Author yang
 *  @Description 验证option对象
**/
func (server *Server) ServeCon(conn io.ReadWriteCloser) {
	defer func() { _ = conn.Close() }()
	var opt Option
	// @Todo 通过 json.NewDecoder反序列化得到Option对象
	if err := json.NewDecoder(conn).Decode(&opt); err != nil {
		log.Println("rpc server: options error:", err)
		return
	}
	// @Todo 检查MagicNumber
	if opt.MagicNumber != MagicNumber {
		log.Printf("rpc server: MagicNumber is error:	%x \n", opt.MagicNumber)
		return
	}
	// @Todo 检查CodeType对象是否准确
	f := codec.NewCodecFuncMap[opt.CodecType]
	if f == nil {
		log.Printf("rpc server: CodeType is error:	%x \n", opt.CodecType)
		return
	}

	log.Printf("验证option对象通过 \n")

	// @Todo 通过不同的编码构造器处理请求
	server.serveCodec(f(conn))
}

// invalidRequest is a placeholder for response argv when error occurs
var invalidRequest = struct{}{}

/**
 *  @Author yang
 *  @Description：服务构造器
				读取请求：readRequest ； 处理请求：handleRequest；响应请求：sendResponse
**/
func (server *Server) serveCodec(cc codec.Codec) {
	sending := new(sync.Mutex) // 确保响应的完整
	wg := new(sync.WaitGroup)
	for {
		req, err := server.readRequest(cc)
		if err != nil {
			if req == nil {
				break
			}
			req.h.Error = err.Error()
			server.sendResponse(cc, req.h, invalidRequest, sending)
			continue
		}
		wg.Add(1)
		go server.handleRequest(cc, req, sending, wg)
	}
}

type request struct {
	h            *codec.Header // header of request
	argv, replyv reflect.Value // argv and replyv of reque
}

/**
 *  @Author yang
 *  @Description 读取请求
**/
func (server *Server) readRequest(cc codec.Codec) (*request, error) {
	// @Todo 读取header
	h, err := server.readRequestHeader(cc)
	if err != nil {
		return nil, err
	}
	req := &request{h: h}
	// TODO: now we don't know the type of request argv
	// day 1, just suppose it's string
	req.argv = reflect.New(reflect.TypeOf(""))
	if err = cc.ReadBody(req.argv.Interface()); err != nil {
		log.Println("rpc server: read argv err:", err)
	}
	return req, nil
}

/**
 *  @Author yang
 *  @Description 读取请求header
**/
func (server *Server) readRequestHeader(cc codec.Codec) (*codec.Header, error) {
	var h codec.Header
	if err := cc.ReadHeader(&h); err != nil {
		if err != io.EOF && err != io.ErrUnexpectedEOF {
			log.Println("rpc server: read header error:", err)
		}
		return nil, err
	}
	return &h, nil
}

/**
 *  @Author yang
 *  @Description 响应请求
**/
func (server *Server) sendResponse(cc codec.Codec, h *codec.Header, body interface{}, sending *sync.Mutex) {
	sending.Lock()
	defer sending.Unlock()
	if err := cc.Write(h, body); err != nil {
		log.Println("【server】rpc server: write response error:", err)
	}
}

/**
 *  @Author yang
 *  @Description 请求处理
**/
func (server *Server) handleRequest(cc codec.Codec, req *request, sending *sync.Mutex, wg *sync.WaitGroup) {
	// TODO, should call registered rpc methods to get the right replyv
	// day 1, just print argv and send a hello message
	defer wg.Done()
	log.Println(req.h, req.argv.Elem())
	req.replyv = reflect.ValueOf(fmt.Sprintf("golang-rpc resp %d", req.h.Seq))
	server.sendResponse(cc, req.h, req.replyv.Interface(), sending)
}
